

// JavaScript 语言中，生成实例对象的传统方法是通过构造函数。下面是一个例子：
function Person() {
    this.name = 'zhichao'
    this.sex = 'nan'
}
Person.prototype.say = function (msg) {
    console.log('说话：', msg);  // 说话： 你好
}
let p1 = new Person()
p1.say('你好')
console.log('--------------------------------');

// 通过class关键字，可以定义类。
class Person2 {
    name = '志超'
    sex = '男'
    say(msg) {
        console.log(`个人信息:${this.name}:${this.sex}`); // 个人信息:志超:男
        console.log('msg:', msg); // msg: 你好hi
    }
}
let p2 = new Person2()
console.log(p2.name); // 志超
console.log(p2.sex); // 男
p2.say('你好hi')
console.log('--------------------------------');

// constructor() 方法
class Person3 {
    name
    sex
    constructor(name, sex) {
        console.log('new 对象的时候用');
        this.name = name
        this.sex = sex
    }
    say3(msg) {
        console.log(`个人信息:${this.name},${this.sex}`)
        console.log(`${this.name}说:`, msg)
    }
}
let p3 = new Person3('崔梦娇', '小仙女')
console.log('name:' + p3.name);
console.log('sex:', p3.sex);
p3.say3('我崔梦娇喜欢看腹肌大帅哥！！！')
console.log('--------------------------------');


// 处理复杂的赋值结构，当构造函数中需要赋值的属性比较多的时候，
// 可以采用【解构 + 传对象】的方式简化赋值
class Person4 {
    name
    sex
    birthday
    id
    constructor(user) {
        console.log('new 对象的时候用');
        // 普通赋值语法
        // this.name = user.name
        // this.sex = user.sex
        // this.birthday = user.birthday
        // this.id = user.id
        // 把赋值逻辑改为解构赋值方法
        ({ name: this.name, sex: this.sex, birthday: this.birthday, id: this.id } = user);
    }
}
// 定义一个对象，实例化的时候传输这个对象到构造函数constructor 中:
let userobj = {
    name: 'liqi',
    sex: 'superman',
    birthday: '2002-11-18',
    id: '130113200211181130'
};
let p4 = new Person4(userobj)
console.log(p4);
console.log('name:' + p4.name); // name:liqi
console.log('sex:', p4.sex); // sex: superman
console.log('birthday:', p4.birthday); // birthday: 2002-11-18
console.log('id:', p4.id); // id: 130113200211181130
console.log('--------------------------------');



// 私有方法和私有属性
// ES2022正式为class添加了私有属性，方法是在属性名之前使用#表示。
// - 私有属性和私有方法 只能在类中通过this调用

class Foo {
    #a; // 私有属性
    #b;
    constructor(a, b) {
        this.#a = a;
        this.#b = b
    }
    #sum() {
        return (this.#a + ',' + this.#b)
    }
    printSum() {
        console.log(this.#sum()); // lwj,lq
    }
    // 使用存取器调用私有属性、给私有属性赋值
    get a() {
        return this.#a
    }
    set a(value) {
        this.#a = value
    }
    get b() {
        return this.#b
    }
    set b(value) {
        this.#b = value
    }
}
let fooobj = new Foo('lwj', 'lq')
fooobj.printSum()
// fooObj1.#a  // 不能直接调用私有属性
// fooObj1.#sum // 不能直接调用私有属性
// 使用存取器调用
console.log(fooobj.a); // lwj
console.log(fooobj.b); // lq
fooobj.a = 'liaowenjing'
fooobj.b = 'chouliqi'
console.log(fooobj.a); // liaowenjing
console.log(fooobj.b); // chouliqi

console.log('--------------------------------');

// 取值函数（getter）和存值函数（setter）
// 基本语法如下：
class MyClass {
    constructor() {
        // ...
    }
    #num = 0
    get prop() {
        return 'getterValue:' + this.#num
    }
    set prop(value) {
        this.#num = value
    }
}
let m = new MyClass();
m.prop
console.log(m.prop); // getterValue:0
m.prop = 123
console.log(m.prop); // getterValue: 123
console.log('--------------------------------');



// 静态属性和静态方法

// 静态属性
// 静态属性指的是 Class 本身的属性，即Class.propName，而不是定义在实例对象（this）上的属性。

// 基本语法：
class Foo1 {
    static prop = 11;
}
// 调用：
// 静态属性只能使用类名直接调用：
let fooObj = new Foo1()
// 使用对象调用静态属性获取不到
console.log(fooObj.prop); // 获取不到值，返回结果为 undefined
console.log(Foo1.prop); // 11
console.log('--------------------------------');


// 静态方法

// 基本语法：
class Foo2 {
    static say() {
        console.log('静态方法: say'); // 静态方法: say
    }
}

// 调用：
// 静态方法只能使用类名直接调用：
Foo2.say()
console.log('--------------------------------');

// 使用注意事项

// - 普通方法中的this 指向的是调用它的对象
class Foo3 {
    static name = '静态属性name'
    // 普通方法baz
    baz() {
        console.log('普通方法baz');
    }

    bar() {
        // 普通方法中的this指向调用它的实例对象
        this.baz()
        console.log('普通方法bar');
    }
    // 静态方法bar
    static bar() {
        // 静态方法bar调用了this.baz，这里的this指的是Foo类，而不是Foo的实例,
        // 所以调用的是静态方法baz
        this.baz();  // 静态方法中的 this指的是类，而不是实例
        Foo3.baz()
    }
    // 静态方法baz
    static baz() {
        console.log('静态方法baz');
    }

}
let f3 = new Foo3()
f3.bar() // 调用普通方法  用实例对象调用
Foo3.bar() // 调用静态方法  只能使用类名直接调用
console.log('--------------------------------');


// 类的继承
class Foo4 {

    #name = '父类私有属性#name'
    static fsex = '父类的静态属性fsex'
    fsex = '父类普通属性fsex'
    get name() {
        return this.#name
    }
    set name(value) {
        this.#name = value
    }
    static fbar() {
        this.fbaz();
    }
    static fbaz() {
        console.log('父类的静态方法fbaz');
    }
    fbaz() {
        console.log('父类的普通方法baz---' + this.#name);
    }
    fbar() {
        this.fbaz()
    }
}
class F4 extends Foo4 {

}

let f4 = new F4()
// 调用父类的获取器
console.log(f4.name); // 父类私有属性#name
// 调用父类的存储器 -> 修改内容
f4.name = '修改：父类属性'
// 调用父类的获取器 -> 查看修改的内容
console.log(f4.name); // 修改：父类属性
// 调用父类的普通属性
console.log(f4.fsex); // 父类普通属性fsex
// 调用父类的普通方法
f4.fbaz() // 父类的普通方法baz---修改：父类属性
// 调用父类的静态方法
F4.fbar() // 父类的静态方法fbaz
// 调用父类的静态属性
console.log(F4.fsex); // 父类的静态属性fsex

console.log('-------------------------');
console.log('-------------------------');

// 继承注意事项 和 super关键字

// 当子类有构造函数时，必须首先调用 super()，以初始化父类的属性

class Foo5 {
    static sBar = '父类的静态属性 sBar'
    #name = '父类的私有属性 #name'
    sex = '父类的普通属性 sex'
    age = 22
    constructor(msg) {
        console.log(msg);
    }
    get name() {
        return this.#name
    }
    set name(value) {
        this.#name = value
    }
    static bar() {
        this.baz()
    }
    static baz() {
        console.log('父类的静态方法baz');
    }
    bar() {
        console.log('父类的普通方法 + ' + this.#name);
    }
    baz() {
        this.bar() // ->调用类的普通方法bar
    }
    fu() {
        console.log('只有父类有这个方法fu');
    }

}
class F5 extends Foo5 {
    name
    // sex = '子类的普通属性sex'
    constructor(name) {
        super('调用了父类的构造函数')
        this.name = name
    }
    zi() {
        console.log('-------------------------');
        // 1. 使用 super 关键字调用父类的方法和存取器
        super.baz() // -> 调用父类的普通方法baz() // 父类的普通方法 + 父类的私有属性 #name

        console.log(super.name); // 调用父类的get获取器  父类的私有属性 #name
        super.name = '修改了的-父类的私有属性 #name' // 调用父类的set存储器 修改了值
        console.log(super.name);// 调用父类的get获取器 查看了修改值  // 修改了的-父类的私有属性 #name

        // 2. 有同名属性和方法的情况下，this关键字调用的是子类的属性和方法
        this.baz() // 子类的普通方法baz
        console.log(this.name); // liqi


        // 3. 子类不存在，只有父类的有的属性和方法，this关键字调用父类的方法
        console.log(this.age); // 22
        this.fu() // 只有父类有这个方法fu

        // 4. 父类的属性 使用super关键字调用不到
        console.log(super.sex); // undefined
        // console.log(this.sex); 


    }
    baz() {
        console.log('子类的普通方法baz');
    }
}

let f5 = new F5('liqi')
console.log(f5); // { age: '父类的普通属性 age', name: 'liqi', sex: '子类的sex' }
f5.zi()



console.log('*************************');
console.log('*************************');


class Person5 {
    name
    sex
    constructor(name, sex) {
        this.name = name
        this.sex = sex
        // console.log('病人构造函数被调用了');

    }
    say(msg) {
        console.log(`姓名：${this.name},性别：${this.sex} ,说：${msg}`);

    }
}
class Child extends Person5 {
    bingzheng
    bingfang
    constructor(bz, bf, ...arg) {
        super(...arg);
        this.bingzheng = bz
        this.bingfang = bf
    }
    sayHi(status) {
        console.log(`${this.bingfang}, ${this.bingzheng}`);
        this.say(status)
    }
}

let br1 = new Child('神经病', '1号房', '志超', '男')
br1.sayHi('我在发神经')
let br2 = new Child('便秘症', '2号房', '梦娇', '小仙女')
br2.sayHi('我在拉屎')

console.log('-------------------------');

let c1 = new Person5('志超', '男')
c1.say('发神经好爽')
let c2 = new Person5('梦娇', '小仙女')
c2.say('拉不出来')

console.log('*************************');
console.log('*************************');

// // // // // // // // // // // // // // // //  // // // // // // // // // // // // // 

class Foo22 {
    // 静态属性
    static sBar = '父类静态属性sBar'
    // 私有属性
    #name = '父类私有属性#name'
    // 普通属性
    sex = '父类普通属性sex'
    // 获取器
    get name1() {
        return this.#name
    }
    // 存储器
    set name1(value) {
        this.#name = value
    }
    // 静态方法bar
    static bar() {
        // 静态方法bar调用了this.baz，这里的this指的是Foo类，而不是Foo的实例，
        this.baz();
    }
    // 静态方法baz
    static baz() {
        console.log('父类的静态方法: baz');
    }
    // 普通方法baz
    baz() {
        console.log('父类: world#######' + this.#name);
    }
    // 普通方法bazf00
    bazf00() {
        // 谁调用就是谁
        this.baz()
    }
}
class SonFoo extends Foo22 {
    ssay() {
        // 2.私有属性和方法不能再派生类中使用，只能在自己的类内容使用
        // this.#name // 属性 "#name" 在类 "Foo22" 外部不可访问，因为它具有专用标识符。
    }
}
/** 
* 
* 1. 子类对象和子类可是使用父类列项：
    父类的普通属性
    父类的普通方法
    
    父类的静态属性（需直接使用子类名调用）
    父类的静态方法（需直接使用子类名调用）
    父类的存取器
*/
// 创建子类的对象
let sf1 = new SonFoo()
// 父类的普通属性
console.log(sf1.sex);
// 父类的普通方法
sf1.bazf00()
// 父类的存取器
sf1.name1 = '刘家锦'
console.log(sf1.name1);
// 父类的静态属性（需直接使用子类名调用）
console.log(SonFoo.sBar);
// 父类的静态方法（需直接使用子类名调用）
SonFoo.baz()

